package com.dm0858.bianmin.mine.tools;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.ColorInt;
import androidx.annotation.DrawableRes;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.core.app.NotificationManagerCompat;

import java.lang.reflect.Field;

/**
 * @description 吐司工具类
 * @param
 * @return
 * @author 迪迪丶Sir
 * @since 2020-03-18
 **/
public class ToastUtils {
    
    private static final int COLOR_DEFAULT = 0xFEFFFFFF;
    private static final String NULL = "null";
    
    private static IToast iToast;
    private static int sGravity = -1;
    private static int sXOffset = -1;
    private static int sYOffset = -1;
    private static int sBackgroundColor = COLOR_DEFAULT;
    private static int sBackgroundResource = -1;
    private static int sMessageColor = COLOR_DEFAULT;
    private static int sMessageTextSize = -1;
    
    public ToastUtils() {
        throw new UnsupportedOperationException("u can't instantiate me");
    }
    
    /**
     * @description 设置吐司的位置
     * @param  
     * @return 
     * @author 迪迪丶Sir
     * @since 2020-03-18
     **/
    public static void setGravity(final int gravity,final int xOffset,final int yOffset) {
        sGravity = gravity;
        sXOffset = xOffset;
        sYOffset = yOffset;
    }

    /**
     * @description 设置背景颜色
     * @param
     * @return
     * @author 迪迪丶Sir
     * @since 2020-03-18
     **/
    public static void setBackgroundColor(@ColorInt final int backgroundColor) {
        sBackgroundColor = backgroundColor;
    }

    /**
     * @description 设置背景资源
     * @param
     * @return
     * @author 迪迪丶Sir
     * @since 2020-03-18
     **/
    public static void setBackgroundResource(@DrawableRes final int backgroundResource) {
        sBackgroundResource = backgroundResource;
    }

    /**
     * @description 设置消息颜色
     * @param
     * @return
     * @author 迪迪丶Sir
     * @since 2020-03-18
     **/
    public static void setMessageColor(@ColorInt final int messageColor) {
        sMessageColor = messageColor;
    }

    /**
     * @description 设置字体大小
     * @param
     * @return
     * @author 迪迪丶Sir
     * @since 2020-03-18
     **/
    public static void setMessageTextSize(final int textSize) {
        sMessageColor = ConversionUtils.sp2px(textSize);
    }
    
    /**
     * @description 短时间的吐司
     * @param  
     * @return 
     * @author 迪迪丶Sir
     * @since 2020-03-18
     **/
    public static void showShort(final CharSequence text) {
        show(text == null ? NULL :text, Toast.LENGTH_SHORT);
    }

    /**
     * @description 短时间的吐司
     * @param  
     * @return 
     * @author 迪迪丶Sir
     * @since 2020-03-18
     **/
    public static void showShort(@StringRes final int resId) {
        show(resId,Toast.LENGTH_SHORT);
    }

    /**
     * @description 短时间的吐司
     * @param  
     * @return 
     * @author 迪迪丶Sir
     * @since 2020-03-18
     **/
    public static void showShort(@StringRes final int resId,final Object... args) {
        show(resId,Toast.LENGTH_SHORT,args);
    }

    /**
     * @description 短时间的吐司
     * @param
     * @return
     * @author 迪迪丶Sir
     * @since 2020-03-18
     **/
    public static void showShort(final String format,final Object... args) {
        show(format,Toast.LENGTH_SHORT,args);
    }

   /*******************/

    /**
     * @description 长时间的吐司
     * @param
     * @return
     * @author 迪迪丶Sir
     * @since 2020-03-18
     **/
    public static void showLong(final CharSequence text) {
        show(text == null ? NULL :text, Toast.LENGTH_LONG);
    }

    /**
     * @description 长时间的吐司
     * @param
     * @return
     * @author 迪迪丶Sir
     * @since 2020-03-18
     **/
    public static void showLong(@StringRes final int resId) {
        show(resId,Toast.LENGTH_LONG);
    }

    /**
     * @description 长时间的吐司
     * @param
     * @return
     * @author 迪迪丶Sir
     * @since 2020-03-18
     **/
    public static void showLong(@StringRes final int resId,final Object... args) {
        show(resId,Toast.LENGTH_LONG,args);
    }

    /**
     * @description 长时间的吐司
     * @param
     * @return
     * @author 迪迪丶Sir
     * @since 2020-03-18
     **/
    public static void showLong(final String format,final Object... args) {
        show(format,Toast.LENGTH_LONG,args);
    }

    /**
     * @description 显示短时间自定义吐司
     * @param  
     * @return 
     * @author 迪迪丶Sir
     * @since 2020-03-18
     **/
    public static View showCustomShort(@LayoutRes final int layoutId) {
        return showCustomShort(getView(layoutId));
    }

    public static View showCustomShort(final View view) {
        show(view,Toast.LENGTH_SHORT);
        return view;
    }

    /**
     * @description 显示长时间自定义吐司
     * @param
     * @return
     * @author 迪迪丶Sir
     * @since 2020-03-18
     **/
    public static View showCustomLong(@LayoutRes final int layoutId) {
        return showCustomLong(getView(layoutId));
    }

    public static View showCustomLong(final View view) {
        show(view,Toast.LENGTH_LONG);
        return view;
    }

    /**
     * @description 取消吐司
     * @param
     * @return
     * @author 迪迪丶Sir
     * @since 2020-03-18
     **/
    public static void cancel() {
        if (iToast != null) iToast.cancel();
    }

    /**
     * @description 显示吐司
     * @param
     * @return
     * @author 迪迪丶Sir
     * @since 2020-03-18
     **/
    private static void show(final int resId,final int duration) {
        show(resId,duration,(Object) null);
    }

    private static void show(final int resId,final int duration,final Object... args) {
        try {
            CharSequence text = Utils.getApp().getResources().getText(resId);
            if (args != null) text = String.format(text.toString(),args);
            show(text,duration);
        } catch (Exception e) {
            show(String.valueOf(resId),duration);
        }
    }

    private static void show(final String format,final int duration,final Object... args) {
        String text = format;
        if (text == null) text = NULL;
        else if (args != null) text = String .format(format,args);
        show(text,duration);
    }

    private static void show(final CharSequence text,final int duration) {
        Utils.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                cancel();
                iToast = ToastFactory.makeToast(Utils.getApp(),text,duration);
                final View toastView = iToast.getView();
                if (toastView == null) return;
                final TextView tvMessage = toastView.findViewById(android.R.id.message);
                if (sMessageColor != COLOR_DEFAULT) tvMessage.setTextColor(sMessageColor);
                if (sMessageTextSize != -1) tvMessage.setTextSize(sMessageTextSize);
                if (sGravity != -1 || sXOffset != -1 || sYOffset != -1) iToast.setGravity(sGravity,sXOffset,sYOffset);
                setBackground(tvMessage);
                iToast.show();
            }
        });
    }

    private static void show(final View view,final int duration) {
        Utils.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                cancel();
                iToast = ToastFactory.newToast(Utils.getApp());
                iToast.setView(view);
                iToast.setDuration(duration);
                if (sGravity != -1 && sXOffset != -1 && sYOffset != -1) iToast.setGravity(sGravity,sXOffset,sYOffset);
                setBackground();
                iToast.show();
            }
        });
    }

    private static void setBackground() {
        if (sBackgroundResource != -1) {
            final View toastView = iToast.getView();
            toastView.setBackgroundResource(sBackgroundResource);
        } else if (sBackgroundColor != COLOR_DEFAULT) {
            final View toastView = iToast.getView();
            Drawable background = toastView.getBackground();
            if (background != null) {
                background.setColorFilter(new PorterDuffColorFilter(sBackgroundColor, PorterDuff.Mode.SRC_IN));
            } else {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) toastView.setBackground(new ColorDrawable(sBackgroundColor));
                else toastView.setBackground(new ColorDrawable(sBackgroundColor));
            }
        }
    }

    private static void setBackground(final TextView tvMsg) {
        if (sBackgroundResource != -1) {
            final View toastView = iToast.getView();
            toastView.setBackgroundResource(sBackgroundResource);
            tvMsg.setBackgroundColor(COLOR_DEFAULT);
        } else if (sBackgroundColor != COLOR_DEFAULT) {
            final View toastView = iToast.getView();
            Drawable textViewBackground = toastView.getBackground();
            Drawable messageBackground = tvMsg.getBackground();
            if (textViewBackground != null && messageBackground != null) {
                textViewBackground.setColorFilter(new PorterDuffColorFilter(sBackgroundColor,PorterDuff.Mode.SRC_IN));
                tvMsg.setBackgroundColor(Color.TRANSPARENT);
            } else if (textViewBackground != null) {
                textViewBackground.setColorFilter(new PorterDuffColorFilter(sBackgroundColor,PorterDuff.Mode.SRC_IN));
            } else if (messageBackground != null) {
                messageBackground.setColorFilter(new PorterDuffColorFilter(sBackgroundColor,PorterDuff.Mode.SRC_IN));
            } else {
                toastView.setBackgroundColor(sBackgroundColor);
            }
        }
    }

    private static View getView(@LayoutRes final int layoutId) {
        LayoutInflater inflater = (LayoutInflater) Utils.getApp().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        return inflater.inflate(layoutId,null);
    }

    static class ToastFactory {

        static IToast makeToast(Context context,CharSequence text,int duration) {
            if (NotificationManagerCompat.from(context).areNotificationsEnabled()) {
                return new SystemToast(makeNormalToast(context,text,duration));
            }
            return new ToastWithoutNotification(makeNormalToast(context,text,duration));
        }

        static IToast newToast(Context context) {
            if (NotificationManagerCompat.from(context).areNotificationsEnabled()) {
                return new SystemToast(new Toast(context));
            }
            return new ToastWithoutNotification(new Toast(context));
        }

        private static Toast makeNormalToast(Context context,CharSequence text,int duration) {
            @SuppressLint("ShowToast")
            Toast toast  = Toast.makeText(context,"",duration);
            toast.setText(text);
            return toast;
        }
    }

    static class SystemToast extends AbsToast {

        SystemToast(Toast toast) {
            super(toast);
            if (Build.VERSION.SDK_INT == Build.VERSION_CODES.ECLAIR_MR1) {
                try {
                    Field mTNField = Toast.class.getDeclaredField("mTN");
                    mTNField.setAccessible(true);
                    Object mTN = mTNField.get(toast);
                    Field mTNmHandlerField = mTNField.getType().getDeclaredField("mHandler");
                    mTNmHandlerField.setAccessible(true);
                    Handler tnHandler = (Handler) mTNmHandlerField.get(mTN);
                    mTNmHandlerField.set(mTN,new SafeHandler(tnHandler));
                } catch (Exception ignored) {

                }
            }
        }

        @Override
        public void show() {
            mToast.show();
        }

        @Override
        public void cancel() {
            mToast.cancel();
        }

        static class SafeHandler extends Handler {
            private Handler handler;

            SafeHandler(Handler handler) {
                this.handler = handler;
            }

            @Override
            public void handleMessage(@NonNull Message msg) {
                handler.handleMessage(msg);
            }

            @Override
            public void dispatchMessage(@NonNull Message msg) {
                try {
                    handler.dispatchMessage(msg);
                } catch (Exception e) {
                    Log.e("ToastUtils",e.toString());
                }
            }
        }
    }

    static class ToastWithoutNotification extends AbsToast {

        private View mView;
        private WindowManager windowManager;

        private WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();

        private static final Utils.OnActivityDestroyedListener LISTENER =
                new Utils.OnActivityDestroyedListener() {
                    @Override
                    public void onActivityDestroyed(Activity activity) {
                        if (iToast == null) return;
                        activity.getWindow().getDecorView().setVisibility(View.GONE);
                        iToast.cancel();
                    }
                };

        ToastWithoutNotification(Toast toast) {
            super(toast);
        }

        public void show() {
            Utils.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    realShow();
                }
            });
        }

        private void realShow() {
            if (mToast == null) return;
            mView = mToast.getView();
            if (mView == null) return;
            final Context context = mToast.getView().getContext();
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1) {
                windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
                layoutParams.type = WindowManager.LayoutParams.TYPE_TOAST;
            } else {
                Context topActivityOrApp = Utils.getTopActivityOrApp();
                if (!(topActivityOrApp instanceof Activity)) {
                    Log.e("ToastUtils", "Couldn't get top Activity.");
                    return;
                }
                Activity topActivity = (Activity) topActivityOrApp;
                if (topActivity.isFinishing() || topActivity.isDestroyed()) {
                    Log.e("ToastUtils", topActivity + " is useless");
                    return;
                }
                windowManager = topActivity.getWindowManager();
                layoutParams.type = WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
                Utils.getActivityLifecycle().addOnActivityDestroyedListener(topActivity, LISTENER);
            }

            layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
            layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
            layoutParams.format = PixelFormat.TRANSLUCENT;
            layoutParams.windowAnimations = android.R.style.Animation_Toast;
            layoutParams.setTitle("ToastWithoutNotification");
            layoutParams.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
                    | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                    | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
            layoutParams.packageName = Utils.getApp().getPackageName();

            layoutParams.gravity = mToast.getGravity();
            if ((layoutParams.gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
                layoutParams.horizontalWeight = 1.0f;
            }
            if ((layoutParams.gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
                layoutParams.verticalWeight = 1.0f;
            }

            layoutParams.x = mToast.getXOffset();
            layoutParams.y = mToast.getYOffset();
            layoutParams.horizontalMargin = mToast.getHorizontalMargin();
            layoutParams.verticalMargin = mToast.getVerticalMargin();

            try {
                if (windowManager != null) {
                    windowManager.addView(mView, layoutParams);
                }
            } catch (Exception ignored) {/**/}

            Utils.runOnUiThreadDelayed(new Runnable() {
                @Override
                public void run() {
                    cancel();
                }
            }, mToast.getDuration() == Toast.LENGTH_SHORT ? 2000 : 3500);
        }

        @Override
        public void cancel() {
            try {
                if (windowManager != null) {
                    windowManager.removeViewImmediate(mView);
                }
            } catch (Exception ignored) {/**/}
            mView = null;
            windowManager = null;
            mToast = null;
        }
    }

    static abstract class AbsToast implements IToast {

        Toast mToast;

        AbsToast(Toast toast) {
            mToast = toast;
        }

        @Override
        public void setView(View view) {
            mToast.setView(view);
        }

        @Override
        public View getView() {
            return mToast.getView();
        }

        @Override
        public void setDuration(int duration) {
            mToast.setDuration(duration);
        }

        @Override
        public void setGravity(int gravity, int xOffset, int yOffset) {
            mToast.setGravity(gravity,xOffset,yOffset);
        }

        @Override
        public void setText(int resId) {
            mToast.setText(resId);
        }

        @Override
        public void setText(CharSequence s) {
            mToast.setText(s);
        }
    }

    interface  IToast{
        
        void show();
        
        void cancel();
        
        void setView(View view);
        
        View getView();
        
        void setDuration(int duration);
        
        void setGravity(int gravity, int xOffset, int yOffset);
        
        void setText(@StringRes int resId);
        
        void setText(CharSequence s);
    }
}
